﻿using System;
using System.Diagnostics;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using JetLoginTool.Engine;
using System.Runtime.Remoting.Messaging;

namespace JetLoginTool
{
	public partial class frmMain : Form
	{
		private readonly JetEngine myJetEngine;
		private DateTime newVersionLastChecked;
		private JetSettings mySettings;
		private IAsyncResult newVersionResult = null;
		private delegate string CheckForNewVersionDelegate();
		private delegate void NewVersionCallback();
		private delegate void NewVersionAvailableDelegate(string newVersion, frmNewVersion newVersionForm);
		private delegate void NewVersionOtherResultDelegate(frmNewVersion newVersionForm);
		private static readonly Regex usageRegex = new Regex(@"([-0-9]+)\.", RegexOptions.Compiled);


		private static readonly Icon grayIcon = new Icon(Application.StartupPath + "\\jetib_gray.ico");
		private static readonly Icon normalIcon = new Icon(Application.StartupPath + "\\jetib.ico");

		private delegate void CrossThreadEventDelegate();

		public frmMain()
		{
			InitializeComponent();

			try
			{
				mySettings = JetSettings.loadSettings();
			}
			catch (LocationsInvalidException ex)
			{
				MessageBox.Show("Unable to load the locations. Please check locations.xml\r\nError details:\r\n" + ex.Message, "Unable to load locations", MessageBoxButtons.OK, MessageBoxIcon.Error);
				Application.Exit();
			}

			if (mySettings != null)
			{
				mySettings.SuppressSaving = true;
				myJetEngine = new JetEngine(mySettings);
				myJetEngine.JetEngineHalted += myJetEngine_JetEngineHalted;
				myJetEngine.ResponseCodeChanged += myJetEngine_ResponseCodeChanged;
				myJetEngine.ProcessingCompleted += myJetEngine_ProcessingCompleted;
				myJetEngine.ProcessingStarted += myJetEngine_ProcessingStarted;

				txtUsername.Text = mySettings.Username;
				txtPassword.Text = mySettings.PlainPassword;

				chkSaveUsername.Checked = mySettings.SaveUsername;
				chkSavePassword.Checked = mySettings.SavePassword;

				chkNewVersions.Checked = mySettings.CheckForNewVersions;
				newVersionLastChecked = (mySettings.CheckForNewVersions ? DateTime.MinValue : DateTime.MaxValue);

				chkStartAutomatically.Checked = mySettings.LoginOnStart;
				chkStartMinimised.Checked = mySettings.StartMinimised;
				chkStartWindows.Checked = mySettings.StartWithWindows;

				foreach (Location thisLocation in mySettings.Locations)
				{
					cmbLocation.Items.Add(thisLocation.Name);
				}
				cmbLocation.Text = mySettings.LocationName;

				nudBalloonTimeoutError.Value = mySettings.ErrorBalloonTimeout;
				nudBalloonTimeoutLogin.Value = mySettings.SuccessBalloonTimeout;

				lblCopyright.Text =
					"Jet Login Tool V" + Application.ProductVersion + Environment.NewLine +
					"(c) 2008-" + DateTime.Now.Year + " Daniel Paoliello" + Environment.NewLine + Environment.NewLine +
					"This application is Freeware!" + Environment.NewLine +
					"(So share it with your friends)";
				mySettings.SuppressSaving = false;
			}
			else
			{
				Application.Exit();
			}
		}

		void myJetEngine_ProcessingStarted(object sender, EventArgs e)
		{
			if (!IsDisposed)
			{
				Invoke(new CrossThreadEventDelegate(processingStarted));
			}
		}

		private void processingStarted()
		{
			pgrProcessing.Visible = true;
			lblStatus.Visible = false;
		}

		void myJetEngine_ProcessingCompleted(object sender, EventArgs e)
		{
			if (!IsDisposed)
			{
				Invoke(new CrossThreadEventDelegate(processingCompleted));
			}
		}

		private void processingCompleted()
		{
			pgrProcessing.Visible = false;
			lblStatus.Visible = true;
			if (myJetEngine.ResponseCode == JetResponseCodes.Success)
			{
				lblUsageMonth.Text = "Used this month: " + myJetEngine.UsageMonth;
				lblUsageRemains.Text = "Amount remaining: " + myJetEngine.UsageRemains;
				lblUsageSession.Text = "Used this session: " + myJetEngine.UsageSession;

				Match usageMonthMatch = usageRegex.Match(myJetEngine.UsageMonth);
				Match usageRemainsMatch = usageRegex.Match(myJetEngine.UsageRemains);
				Match usageSessionMatch = usageRegex.Match(myJetEngine.UsageSession);

				if ((usageMonthMatch.Success) && (usageRemainsMatch.Success) && (usageSessionMatch.Success))
				{
					int usageMonth = Int32.Parse(usageMonthMatch.Groups[1].Value);
					int usageSession = Int32.Parse(usageSessionMatch.Groups[1].Value);
					int usageRemains = Int32.Parse(usageRemainsMatch.Groups[1].Value);

					int totalUsage = usageMonth + usageSession;
					int totalQuota = usageMonth + usageSession + usageRemains;
					ntfyMain.Text = "Jet Login Tool\r\nUsage: " + totalUsage + "/" + totalQuota;
				}
			}
		}

		void myJetEngine_ResponseCodeChanged(object sender, EventArgs e)
		{
			if (!IsDisposed)
			{
				Invoke(new CrossThreadEventDelegate(responseCodeChanged));
			}
		}

		private void responseCodeChanged()
		{
			ntfyMain.Icon = grayIcon;

			switch (myJetEngine.ResponseCode)
			{
				case JetResponseCodes.Success:
					lblStatus.Text = "Logged in Successfully";
					if ((WindowState == FormWindowState.Minimized) && (mySettings.SuccessBalloonTimeout > 0))
					{
						ntfyMain.ShowBalloonTip(mySettings.SuccessBalloonTimeout, "Logged In", "Jet Login Tool has successfully logged in", ToolTipIcon.Info);
					}

					if ((DateTime.Now - newVersionLastChecked).Hours > 1)
					{
						checkForUpdates(false);
					}
					ntfyMain.Icon = normalIcon;
					break;
				case JetResponseCodes.UnknownError:
					lblStatus.Text = "Unknown Error Occured!!!";
					break;
				case JetResponseCodes.NetworkError:
					lblStatus.Text = "Network Error - Will try again";
					break;
				case JetResponseCodes.BadUsernamePassword:
					lblStatus.Text = "Bad username or password";
					break;
				case JetResponseCodes.JetBroken:
					lblStatus.Text = "Jet appears to be broken - Will try again";
					break;
				case JetResponseCodes.JetProcessing:
					lblStatus.Text = "Jet is 'processing' - Will try again";
					break;
				case JetResponseCodes.InternetOkNotLoggedIn:
					lblStatus.Text = "Jet not logged in, but Internet ok";
					if ((DateTime.Now - newVersionLastChecked).Hours > 1)
					{
						checkForUpdates(false);
					}
					ntfyMain.Icon = normalIcon;
					break;
				case JetResponseCodes.WaitingForNetwork:
					lblStatus.Text = "Waiting for network - Will try again";
					break;
				case JetResponseCodes.None:
					lblStatus.Text = "No Status (?)";
					break;
				case JetResponseCodes.UserRequestedStop:
					lblStatus.Text = "User requested stop";
					break;
				default:
					lblStatus.Text = "ERROR - You shouldn't see this...";
					break;
			}

			if ((!myJetEngine.IsBusy) && ((myJetEngine.ResponseCode == JetResponseCodes.BadUsernamePassword) || (myJetEngine.ResponseCode == JetResponseCodes.UnknownError) || (myJetEngine.ResponseCode == JetResponseCodes.NetworkError)))
			{
				ShowErrorBalloon();
			}
		}

		private void ShowErrorBalloon()
		{
			if ((WindowState == FormWindowState.Minimized) && (mySettings.ErrorBalloonTimeout > 0))
			{
				ntfyMain.ShowBalloonTip(mySettings.ErrorBalloonTimeout, "Jet Engine Halted", "The Jet Login Tool has stopped.\r\n Please check the log to find out why.\r\n\r\n(Click to open window).", ToolTipIcon.Warning);
			}
		}

		void myJetEngine_JetEngineHalted(object sender, EventArgs e)
		{
			try
			{
				Invoke(new CrossThreadEventDelegate(jetEngineHalted));
			}
			catch (ObjectDisposedException)
			{ }
		}

		private void jetEngineHalted()
		{
			ntfyMain.Icon = grayIcon;
			SetEnableOnElements(true);
			btnGo.Text = "Go";
		}

		private void btnExit_Click(object sender, EventArgs e)
		{
			myJetEngine.stopAutoLogin();
			Application.Exit();
		}

		private void btnGo_Click(object sender, EventArgs e)
		{
			if (myJetEngine.IsBusy)
			{
				myJetEngine.stopAutoLogin();
				lblStatus.Text = "Login Stopped.";
			}
			else
			{
				StartAutoLogin();
			}
		}

		private void txtUsername_TextChanged(object sender, EventArgs e)
		{
			txtUsername.BackColor = string.IsNullOrEmpty(txtUsername.Text) ? Color.Red : SystemColors.Window;
		}

		private void txtPassword_TextChanged(object sender, EventArgs e)
		{
			txtPassword.BackColor = string.IsNullOrEmpty(txtPassword.Text) ? Color.Red : SystemColors.Window;
		}

		private void StartAutoLogin()
		{
			if ((txtUsername.BackColor != Color.Red) && (txtPassword.BackColor != Color.Red))
			{
				mySettings.Username = txtUsername.Text;
				mySettings.PlainPassword = txtPassword.Text;

				if (myJetEngine.startAutoLogin())
				{
					btnGo.Text = "Stop";
					SetEnableOnElements(false);
				}
			}
		}

		private void SetEnableOnElements(bool inEnable)
		{
			txtUsername.Enabled = inEnable;
			txtPassword.Enabled = inEnable;
			lblUsageSession.Text = "";
			startToolToolStripMenuItem.Enabled = inEnable;
			stopToolToolStripMenuItem.Enabled = !inEnable;
			chkSavePassword.Enabled = inEnable;
			chkSaveUsername.Enabled = inEnable;
			cmbLocation.Enabled = inEnable;
			btnClearPrivateData.Enabled = inEnable;
		}

		private void ntfyMain_DoubleClick(object sender, EventArgs e)
		{
			showMe();
		}

		private void txt_KeyPress(object sender, KeyPressEventArgs e)
		{
			if (e.KeyChar == (char)Keys.Return)
			{
				btnGo_Click(this, new EventArgs());
			}
		}

		private void showWindowToolStripMenuItem_Click(object sender, EventArgs e)
		{
			showMe();
		}

		private void startToolToolStripMenuItem_Click(object sender, EventArgs e)
		{
			StartAutoLogin();
		}

		private void stopToolToolStripMenuItem_Click(object sender, EventArgs e)
		{
			myJetEngine.stopAutoLogin();
		}

		private void exitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			myJetEngine.stopAutoLogin();
			Application.Exit();
		}

		private void hideWindowToolStripMenuItem_Click(object sender, EventArgs e)
		{
			hideMe();
		}

		private void ntfyMain_BalloonTipClicked(object sender, EventArgs e)
		{
			showMe();
		}

		private void chkSaveUsername_CheckedChanged(object sender, EventArgs e)
		{
			mySettings.SaveUsername = chkSaveUsername.Checked;
		}

		private void chkSavePassword_CheckedChanged(object sender, EventArgs e)
		{
			mySettings.SavePassword = chkSavePassword.Checked;
		}

		private void checkForUpdates(bool showNoNewUpdates)
		{
			if (newVersionResult == null)
			{
				frmNewVersion newVersionDialog = null;
				if (showNoNewUpdates)
				{
					newVersionDialog = new frmNewVersion();
				}

				CheckForNewVersionDelegate newVersionDelegate = JetEngineVersion.CheckForNewVersion;
				newVersionResult = newVersionDelegate.BeginInvoke(CheckForUpdatesCallback, newVersionDialog);
			}
		}

		private void CheckForUpdatesCallback(IAsyncResult result)
		{
			AsyncResult asyncResult = (AsyncResult)result;
			string version = ((CheckForNewVersionDelegate)asyncResult.AsyncDelegate).EndInvoke(result);
			frmNewVersion newVersionForm = (frmNewVersion)result.AsyncState;

			if (!string.IsNullOrEmpty(version))
			{
				if (version != Application.ProductVersion)
				{
					Invoke(new NewVersionAvailableDelegate(NewVersionAvailable), version, newVersionForm);
				}
				else
				{
					Invoke(new NewVersionOtherResultDelegate(NoNewVersionAvailable), newVersionForm);
				}
			}
			else
			{
				Invoke(new NewVersionOtherResultDelegate(UpdateCheckFailed), newVersionForm);
			}

			newVersionResult = null;
			newVersionLastChecked = DateTime.Now;
		}

		private void NewVersionAvailable(string versionNumber, frmNewVersion newVersionForm)
		{
			if (newVersionForm == null)
			{
				newVersionForm = new frmNewVersion();
			}

			newVersionForm.NewVersionAvailable(versionNumber);
		}

		private void NoNewVersionAvailable(frmNewVersion newVersionForm)
		{
			if (newVersionForm != null)
			{
				newVersionForm.Close();
				MessageBox.Show("Latest version currently installed", "Up to date", MessageBoxButtons.OK, MessageBoxIcon.Information);
			}
		}

		private void UpdateCheckFailed(frmNewVersion newVersionForm)
		{
			string title = "Error checking for updates";
			string text = "There was an error checking for updates.\r\nAre you sure that the internet is actually working?";

			if (newVersionForm != null)
			{
				newVersionForm.Close();
				MessageBox.Show(text, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			else
			{
				ntfyMain.ShowBalloonTip(mySettings.ErrorBalloonTimeout, title, text, ToolTipIcon.Warning);
			}
		}
		
		private void btnCheckUpdatesNow_Click(object sender, EventArgs e)
		{
			checkForUpdates(true);
		}

		private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (e.CloseReason == CloseReason.UserClosing)
			{
				e.Cancel = true;
				hideMe();
			}
			else
			{
				if (myJetEngine != null)
				{
					myJetEngine.stopAutoLogin();
					((IDisposable)myJetEngine).Dispose();
				}
			}
		}

		private void lnkJetLoginCodeplex_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			Process.Start("http://www.codeplex.com/JetLoginTool/");
		}

		private void showMe()
		{
			Visible = true;
			WindowState = FormWindowState.Normal;
		}

		private void hideMe()
		{
			WindowState = FormWindowState.Minimized;
		}

		private void chkStartAutomatically_CheckedChanged(object sender, EventArgs e)
		{
			mySettings.LoginOnStart = chkStartAutomatically.Checked;
		}

		private void chkStartMinimised_CheckedChanged(object sender, EventArgs e)
		{
			mySettings.StartMinimised = chkStartMinimised.Checked;
		}

		private void chkStartWindows_CheckedChanged(object sender, EventArgs e)
		{
			mySettings.StartWithWindows = chkStartWindows.Checked;
			StartUpHelper.toggleStartWithWindows(mySettings.StartWithWindows);
		}

		private void frmMain_Load(object sender, EventArgs e)
		{
			if (mySettings == null)
			{
				Application.Exit();
			}
			else
			{
				if (string.IsNullOrEmpty(txtUsername.Text))
				{
					txtUsername.Select();
				}
				else if (string.IsNullOrEmpty(txtPassword.Text))
				{
					txtPassword.Select();
				}
				else
				{
					btnGo.Select();
					if (mySettings.StartMinimised)
					{
						hideMe();
					}

					if (mySettings.LoginOnStart)
					{
						btnGo_Click(this, new EventArgs());
					}
				}
			}
		}

		private void chkNewVersions_CheckedChanged(object sender, EventArgs e)
		{
			mySettings.CheckForNewVersions = chkNewVersions.Checked;
			newVersionLastChecked = (mySettings.CheckForNewVersions ? DateTime.MinValue : DateTime.MaxValue);
		}

		private void frmMain_SizeChanged(object sender, EventArgs e)
		{
			if (WindowState == FormWindowState.Minimized)
			{
				ShowInTaskbar = false;
				Visible = false;
				hideWindowToolStripMenuItem.Enabled = false;
				showWindowToolStripMenuItem.Enabled = true;
			}
			else
			{
				ShowInTaskbar = true;
				Visible = true;
				hideWindowToolStripMenuItem.Enabled = true;
				showWindowToolStripMenuItem.Enabled = false;
			}
		}

		private void cmbLocation_SelectedIndexChanged(object sender, EventArgs e)
		{
			mySettings.LocationName = cmbLocation.Text;
		}

		private void btnClearPrivateData_Click(object sender, EventArgs e)
		{
			if (MessageBox.Show("This will clear the username and password from the programs memory.\r\nYou will need to re-enter these in order to log in.\r\nContinue?", "Are you sure?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
			{
				mySettings.Username = "";
				mySettings.PlainPassword = "";
				txtUsername.Text = "";
				txtPassword.Text = "";
			}
		}

		private void nudBalloonTimeout_ValueChanged(object sender, EventArgs e)
		{
			mySettings.SuccessBalloonTimeout = Convert.ToInt32(nudBalloonTimeoutLogin.Value);
			mySettings.ErrorBalloonTimeout = Convert.ToInt32(nudBalloonTimeoutError.Value);
		}
	}
}
